package com.zho.util.rctrl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.TreeMap;

import com.zho.util.Helpers;

class Service extends Thread {

	private static Map methods;
	private static Map helps;
	private static Properties aProperty;

	static Map getHelps() {
		return helps;
	}

	static void prepareMethods(String strPropFileName) {
		try {
			methods = new TreeMap();
			helps = new TreeMap();
			aProperty = Helpers.getProperties(strPropFileName);
			Enumeration keys = aProperty.propertyNames();
			while (keys.hasMoreElements()) {
				String key = (String) keys.nextElement();
				if (key.startsWith("CMD.")) {
					String commandName = key.substring(4);
					String commandClassName = aProperty.getProperty(key);
					Class cls = Class.forName(commandClassName);
					Method instanceMethod = cls.getDeclaredMethod("instance",
							new Class[] {});
					Method helpMethod = cls.getDeclaredMethod("help",
							new Class[] {});
					String help = (String) helpMethod.invoke(null,
							new Object[] {});
					methods.put(commandName, instanceMethod);
					helps.put(commandName, help);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e.toString());
		}
	}

	private Socket socket;
	private BufferedReader reader;
	private PrintStream writer;
	private InetAddress peer;
	private boolean stop;
	private String loginTime;

	String getPeerInfo() {
		return "LOGIN_TIME:" + loginTime + "; IP:" + peer.getHostAddress()
				+ "; HOST:" + peer.getHostName() + "; ACTIVE:" + this.isAlive();
	}

	Service(Socket socket) throws IOException {
		this.stop = false;
		SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
		this.loginTime = sdf.format(new Date());
		this.socket = socket;
		this.peer = socket.getInetAddress();
		this.reader = new BufferedReader(new InputStreamReader(
				socket.getInputStream()));
		this.writer = new PrintStream(socket.getOutputStream());
	}

	void stopService() {
		stop = true;
	}

	public void run() {
		System.out.println("Session Started:" + getPeerInfo());
		try {
			welcome();
			while (!stop) {
				executeCommand();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			reader.close();
			writer.close();
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		System.out.println("Session Stoped:" + getPeerInfo());
	}

	void welcome() throws IOException {
		writer.println("Welcome to Controller!");
	}

	void executeCommand() throws IOException {

		try {
			writer.print(">");
			writer.flush();
			String inputTxt = reader.readLine();

			writer.println();
			if (inputTxt == null) {
				stopService();
				return;
			}

			inputTxt = inputTxt.trim();
			if (inputTxt.length() == 0)
				return;

			System.out.println("--\"" + inputTxt + "\" received");
			writer.println("--\"" + inputTxt + "\" received");
			StringTokenizer st = new StringTokenizer(inputTxt);
			String commandName;
			String params[];
			Command cmd;

			int paramNum = st.countTokens() - 1;
			if (paramNum < 0)
				return;

			else {
				commandName = (String) st.nextElement();
				params = new String[paramNum];
				for (int i = 0; i < paramNum; i++)
					params[i] = st.nextToken();
			}

			Method instanceMethod = (Method) methods.get(commandName);
			if (instanceMethod == null) {
				writer.println("Unsupported Command!");
				return;
			}

			try {
				cmd = (Command) instanceMethod.invoke(null, new Object[] {});
			} catch (InvocationTargetException e) {
				throw (CommandFailureException) e.getTargetException();
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.toString());
			}

			String returnTxt = cmd.execute(params);
			StringTokenizer rt = new StringTokenizer(returnTxt, "\n");
			while (rt.hasMoreTokens()) {
				writer.println(rt.nextToken());
			}

			if (cmd instanceof QuitCommand) {
				stopService();
			}

		} catch (CommandFailureException e) {
			writer.println("Command Failed!");
			writer.println(e.toString());
		}
	}

}
